
/*
+++Calculator inputs+++
1 Starting portfolio
2 Annual spending
3 stock / bond / cash %
4 Age at retirement
5 male or female
6 pension or other income

other to add:
x-sex
x-toggle death wedge on and off
x-toggles for showing below start, above start and 2x threshold?

url parameter parsing 
x-social security, taxes and investor fees
add health care costs?
joint couple probability of death
x-toggle between real and nominal dollars


x-verify sum of cash,bond,stock=100%
*/ 


flexpct=0.1;

// also maybe do a simple graph where you are fire and other graphs (area graph) where you say you have 50%, 100%, 200% more money than you need
function parseURLparams(){
	//parse URL and set inputs to the right values

	isInIframe = (parent !== window);
	if (isInIframe){
		var thisURL = document.referrer;
	} else{
		var thisURL = window.location.href;		
	}
	
	inputnames=["annualspending", "totalsavings", "retireage", "cyclelength","stockpct","bondpct","cashpct","taxrate","invfees","incomevalue","incomestart","incomeend","expensevalue","expensestart","expenseend","flexpct"];
	urlparamnamesin=["spend","initsav","age","yrs","stockpct","bondpct","cashpct","taxrate","fees","income","incstart","incend","expense","expstart","expend","flexpct"];
	defaultvals=[40000,1000000,40,50,80,18,2,0,0.3,0,50,70,0,50,70,0];
	// inputnames2=["male","infl","showdeath","showlow","show2x","show5x"];
	// urlparams2=["sex","infl","showdeath","showlow","show2x","show5x"];
	// defaultvals2=["M",1,1,1,1,1];

	paramvals=[];
	paramvals2=[];

	parseUrl = new URL(thisURL);
	
	for (var i=0;i<inputnames.length;i++){
		paramvals[i]=parseUrl.searchParams.get(urlparamnamesin[i]);

		if (paramvals[i]==null){
			paramvals[i]=defaultvals[i];
		}
		document.getElementById(inputnames[i]).value=paramvals[i];
	}
	// manually set values
	(parseUrl.searchParams.get("sex")==null?$("#male").prop("checked",true):(parseUrl.searchParams.get("sex")?$("#female").prop("checked",true):$("#male").prop("checked",true)));
	(parseUrl.searchParams.get("infl")==null?$("#real").prop("checked",true):(parseUrl.searchParams.get("infl")?$("#real").prop("checked",true):$("#nominal").prop("checked",true)));
	(parseUrl.searchParams.get("showdeath")==null?$('#showdeath').attr('checked',true):$('#showdeath')[0].checked=parseInt(parseUrl.searchParams.get("showdeath")));
	(parseUrl.searchParams.get("showlow")==null?$('#belowstart').attr('checked',true):$('#belowstart')[0].checked=parseInt(parseUrl.searchParams.get("showlow")));	
	(parseUrl.searchParams.get("show2x")==null?$('#twoXstart').attr('checked',true):$('#twoXstart')[0].checked=parseInt(parseUrl.searchParams.get("show2x")));	
	(parseUrl.searchParams.get("show5x")==null?$('#fiveXstart').attr('checked',true):$('#fiveXstart')[0].checked=parseInt(parseUrl.searchParams.get("show5x")));	
	
}


function getSex(){
	sex = ($("#male").prop("checked")==true?"M":"F");
	sexind=(sex=="M"?0:1);
}

function getIncomeExpense(startincome,pensionstartage,pensionendage,inflation_multiplier,cycleyear){

	var income=0;

	if (cycleyear>=pensionstartage-startage&&cycleyear<=pensionendage-startage){
		income=startincome*inflation_multiplier;
	}

	return income;
}



function calculateSurvival(){
	var deathProb=[];
	
	retireage=parseInt($("#retireage").val());

	if (sex=="M"){
		var deatharray=malesurvival.slice();
	} else if (sex=="F"){
		var deatharray=femalesurvival.slice();
	}

	for (var i=0;i<cycleLength;i++){
			if (i==0){
				deathProb[i]=(1-(1-deatharray[retireage+i]))*100;
			} else {
				deathProb[i]=(100-(100-deathProb[i-1])*(1-deatharray[retireage+i]));
			}
		}
	return deathProb;
}

// function calculateCoupleSurvival(){
// 	deathProb=[];
// 	deatharray=[];
// 	retireage=parseInt(document.getElementById("retireage").value);
	
// 	couple = ['M','F'];
// 	agediff=0;
// 	retireage2=retireage+agediff;

// 	if (couple[0]=="M"){
// 		 deatharray[0]=malesurvival.slice();
// 	} else if (couple[0]=="F"){
// 		 deatharray[0]=femalesurvival.slice();
// 	}
// 	if (couple[1]=="M"){
// 		 deatharray[1]=malesurvival.slice();
// 	} else if (couple[1]=="F"){
// 		 deatharray[1]=femalesurvival.slice();
// 	}

	
// 	for (var j=0;j<2;j++){
// 		for (var i=0;i<cycleLength;i++){
// 			if (j==0){
// 				age=retireage+i;
// 			} else if (j==1){
// 				age=retireage2+i;
// 				if (age>120){
// 					age=120;
// 				}
// 			}

// 			if (i==0){
// 				deathProb.push([]);
// 				deathProb[j][i]=(1-(1-deatharray[j][age]));
// 			} else {
// 				deathProb[j][i]=(1-(1-deathProb[j][i-1])*(1-deatharray[j][age]));
// 			}
// 		}
// 	}

// 	for (var i=0;i<cycleLength;i++){
// 		// calc probability of both dead
// 		deathProb[2]= multArrByScal(multArrs(deathProb[0], deathProb[1]),100);	
// 	}

// 	return deathProb[2];
// }



var grapharray=[];

function calcandplot(){
	
	getSex();
	getInputs();
	calculateHistoricalCycles();
	plotGraph();
}

function getInputs(){

	w = window.innerWidth;
	smallscreen = (w<=760?true:false); 
	startage = parseInt($("#retireage").val());
	beginningstache = parseInt((($("#totalsavings").val()).replace(/\,/g, '')).replace(/\$/g,''));
	annualspend = parseInt(($("#annualspending").val()).replace(/\,/g, '').replace(/\$/g,''));
	
	//populate inputs with localized numbers
	$("#totalsavings").val("$"+beginningstache.toLocaleString('en-US'));
	$("#annualspending").val("$"+annualspend.toLocaleString('en-US'));

	retireyrs=parseInt($("#cyclelength").val());
	cycleLength =retireyrs +1;
	flexpct=parseFloat($("#flexpct").val()/100);
	flexparam = flexpct*100;

	stockpct = parseInt($("#stockpct").val())/100;
	fixedpct = parseInt($("#bondpct").val())/100;
	cashpct = parseInt($("#cashpct").val())/100;
	stockparam = stockpct*100;
	fixedparam = fixedpct*100;
	cashparam = cashpct*100;
	
	inflation_adjustment_flag=$("#real").prop("checked");
	infind=(inflation_adjustment_flag?1:0);

	

	showbelow = $('#belowstart').prop('checked');
	showtwoX = $('#twoXstart').prop('checked');
	showfiveX = $('#fiveXstart').prop('checked');
	showbelowind=(showbelow?1:0);
	showtwoXind=(showtwoX?1:0);
	showfiveXind=(showfiveX?1:0);

	feesparam=parseFloat($("#invfees").val());
	invstfees = feesparam/100;
	taxparam=parseFloat($("#taxrate").val());
	efftaxrate=taxparam/100;

	showdeath=$('#showdeath').prop('checked');
	showdeathind=(showdeath?1:0);

	incomestring=$("#incomevalue").val();
	incomelist=incomestring.split(";");
	incomestartagestring=$("#incomestart").val();
	incomestartagelist=incomestartagestring.split(";");
	incomeendagestring=$("#incomeend").val();
	incomeendagelist=incomeendagestring.split(";");

	for (var i=0;i<incomelist.length;i++){
		incomelist[i]=(isNaN(parseInt(((incomelist[i]).replace(/\,/g, '')).replace(/\$/g,'')))?0:parseInt(((incomelist[i]).replace(/\,/g, '')).replace(/\$/g,'')));
		incomestartagelist[i]=(incomestartagelist[i]==undefined?startage:parseInt(incomestartagelist[i]));
		incomeendagelist[i]=(incomeendagelist[i]==undefined?120:parseInt(incomeendagelist[i]));
	}

	expensestring=$("#expensevalue").val();
	expenselist=expensestring.split(";");
	expensestartagestring=$("#expensestart").val();
	expensestartagelist=expensestartagestring.split(";");
	expenseendagestring=$("#expenseend").val();
	expenseendagelist=expenseendagestring.split(";");

	for (var i=0;i<expenselist.length;i++){
		expenselist[i]=(isNaN(parseInt(((expenselist[i]).replace(/\,/g, '')).replace(/\$/g,'')))?0:parseInt(((expenselist[i]).replace(/\,/g, '')).replace(/\$/g,'')));
		expensestartagelist[i]=(expensestartagelist[i]==undefined?startage:parseInt(expensestartagelist[i]));
		expenseendagelist[i]=(expenseendagelist[i]==undefined?120:parseInt(expenseendagelist[i]));
	}
}

function checkallocationinputs(){
	sum=parseFloat($('#stockpct').val())+parseFloat($('#bondpct').val())+parseFloat($('#cashpct').val());

	if (sum>100.9||sum<99.1||isNaN(sum)){
		$('#stockpct').css('color', 'red');
		$('#bondpct').css('color', 'red');
		$('#cashpct').css('color', 'red');
		$("#errormsg").html("Stock, bond and cash percentages must be whole numbers and add to 100%");
		$('#errormsg').show();
		return;
	} else {
		$('#updateallocation').effect( "highlight", {color:'darkgreen'}, 3500 );
		$('#stockpct').css('color', 'black');
		$('#bondpct').css('color', 'black');
		$('#cashpct').css('color', 'black');
		$('#errormsg').hide();
		calcandplot();
	}

	//validate inputs
	
	
}

function calculateHistoricalCycles(){

	//read in returns data from annmarketdata.js
	var inflation_factor=addArrWScal(inflation_return,1);
	var fixed_income_factor=addArrWScal(fixed_income_return,1);
	var stock_factor=addArrWScal(stock_return,1);
	var stockplusdiv_factor=addArrs(stock_factor,dividend_return); // add stock returns + dividend returns

	//create array of arrays with the right length (cycleLength) and with all different starting years
	//[[1871 array],[1872 array]. . . .[1990 array]] for 25 year cycle (since data goes to 2015).
	// NumCycles is equal to the number of market years - the length of the cycle + 1
	// i.e. if you want to analyze 30 years and there are 145 historical years, that'll be 116 cycles
	var numHistYrs = inflation_factor.length
	numCycles = numHistYrs - cycleLength + 1;

	//array of of length (cycleLength) 
	var inflation_array = [];
	var fixed_array = [];
	var stockplusdiv_array = [];
	var inflation_adjustment_array =[];
	failure_array =[];
	belowstart_array=[];
	twoX_array=[];
	fiveX_array=[];

	cyclesArrayNom=[];  // this is an array that holds all historical cycles (arrays) in Nominal dollars
	var numFailures=0;
	var plotlydata=[];

	// iterate over all cycles (numCycles) each i is one historical cycle (e.g. 25 year period)
	for (var i=0; i<numCycles; i++) { 
		//get returns for one cycle //these arrays change for each historical cycle
		inflation_array = inflation_factor.slice(i,i+cycleLength); 				
		fixed_array = fixed_income_factor.slice(i,i+cycleLength);
		stockplusdiv_array = stockplusdiv_factor.slice(i,i+cycleLength);
		
		var stache=[beginningstache]; //reset stache before starting calculation cycle of returns
		var spending = annualspend; //initialize spending to annual spend
		var failureflag = 0; //initialize failure flag

		//Stache returns cycle - calculate returns across allocation for each year of cycle
		for (var j=0;j<cycleLength;j++){ //cycleLength
			inflation_adjustment_array[j]=inflation_array[j]*(j==0?1:inflation_adjustment_array[j-1]);
			
			var thisyearincome=0;
			for (var k=0;k<incomelist.length;k++){
				thisyearincome+=getIncomeExpense(incomelist[k],incomestartagelist[k],incomeendagelist[k],inflation_adjustment_array[j],j);
			}
			// var income=getIncome(SSincomestart,SSstartage,SSendage,inflation_adjustment_array[j],j)+getIncome(pensionincomestart,pensionstartage,pensionendage,inflation_adjustment_array[j],j);  //j is age
			var thisyearexpense=0;
			for (var k=0;k<expenselist.length;k++){
				thisyearexpense+=getIncomeExpense(expenselist[k],expensestartagelist[k],expenseendagelist[k],inflation_adjustment_array[j],j);
			}


			if (failure_array[j]==undefined){
				failure_array[j]=0;
				belowstart_array[j]=0;
				twoX_array[j]=0;
				fiveX_array[j]=0;
			}
			var spending = spending*inflation_array[j]; //change spending by inflation (this is cumulative, as j cycles through, spending will be multiplied by many elements of inflation array)

			// inflation adjusted graph?
			inflation_adjustment=(inflation_adjustment_flag?inflation_adjustment_array[j]:1);
			if (stache[j]<beginningstache*inflation_adjustment){
				adjspending=spending*(1-flexpct);
			} else {
				adjspending=spending;
			}

			if (stache[j]<=0){
				eoy_stache = stache[j]-(adjspending+thisyearexpense)/(1-efftaxrate)+(thisyearincome)*(1+efftaxrate);   //no returns on a negative stache
				failureflag=1;
				failure_array[j]++;
				
			} else {
				// eoy_stache = stache[j]*cashpct*inflation_array[j] + stache[j]*fixedpct*fixed_array[j]*(1-invstfees) + stache[j]*stockpct*stockplusdiv_array[j]*(1-invstfees)-spending/(1-efftaxrate)+income*(1+efftaxrate);
				
				eoy_stache = (stache[j]-(adjspending+thisyearexpense)/(1-efftaxrate)+(thisyearincome)*(1+efftaxrate))*cashpct*inflation_array[j] + (stache[j]-(adjspending+thisyearexpense)/(1-efftaxrate)+(thisyearincome)*(1+efftaxrate))*fixedpct*fixed_array[j]*(1-invstfees) + (stache[j]-(adjspending+thisyearexpense)/(1-efftaxrate)+(thisyearincome)*(1+efftaxrate))*stockpct*stockplusdiv_array[j]*(1-invstfees);
				if (eoy_stache<0){
					failureflag=1;		//sometimes spending is subtracted and makes stache go negative
					failure_array[j]++;
				} else if (eoy_stache<beginningstache*inflation_adjustment){
					(showbelow ? belowstart_array[j]++ :"");
				} else if (showfiveX && eoy_stache>beginningstache*5*inflation_adjustment){
					// (showfiveX?fiveX_array[j]++:"");
					fiveX_array[j]++;
				} else if (showtwoX && eoy_stache>beginningstache*2*inflation_adjustment){
					// (showtwoX?twoX_array[j]++:"");
					twoX_array[j]++
				}
			}

			//stache value at end of year
			stache.push(eoy_stache)
		}
		//add failure flag to beginning of stache
		stache.unshift(failureflag);
		numFailures+=failureflag;
		// once stache is a full array with length cycleLength+1 (initial value + cycleLength elements)
		// add this full array to cyclesArrayNom
		
		cyclesArrayNom[i]=stache;
	}

	if (showdeath){
		death_prob = calculateSurvival();  //fillArray(cycleLength,0);
 		survival_prob= addArrWScal(multArrByScal(death_prob, -1), 100);
	} else {
		death_prob = fillArray(cycleLength,0); 
		survival_prob= fillArray(cycleLength,100);
	}
	
	broke_array=[]; //<0
	belowthreshold_array=[]; //<1x
	awesome_array=[];  //fiveX
	great_array=[]; //twoX
	okay_array=[];  //>1x
	age_array=[];
	for (var j=0;j<cycleLength;j++){
		age_array[j]=startage+j;
		broke_array[j]=survival_prob[j]*(failure_array[j]/numCycles);
		belowthreshold_array[j]=survival_prob[j]*(belowstart_array[j]/numCycles);
		great_array[j]=survival_prob[j]*(twoX_array[j]/numCycles);
		awesome_array[j]=survival_prob[j]*(fiveX_array[j]/numCycles);
		okay_array[j]=(survival_prob[j]-(broke_array[j])-(great_array[j]+awesome_array[j])-(belowthreshold_array[j]));
	}
	
	// data for graph title
	grapharray[0]=(annualspend/beginningstache*100).toFixed(1)+"%";
	grapharray[1]=parseInt(stockpct*100)+(smallscreen?"/":"%:")+parseInt(fixedpct*100)+(smallscreen?"/":"%:")+parseInt(cashpct*100)+(smallscreen?"":"%");
	grapharray[2]=sex;
	grapharray[3]=((1-numFailures/numCycles)*100).toFixed(0)+"%"

}


function plotGraph(){

	//Success (Balance > 0)
	if (!showbelow && !showtwoX &&!showfiveX){
		var okaylabel="成功的 (余额 > 0)";	
	} else if (!showbelow){
		var okaylabel="余额 > 0";	
	} else {
		var okaylabel="余额 > 初始值";
	}
	//Dead死亡、Broke破产、Bal余额、start初始值
	var dead = {x: age_array, y: death_prob.slice(), name:'死亡', fillcolor: 'rgba(94,94,94,.8)', mode:'none', hoverinfo:'x+name+text', text: formatElements(death_prob,1,"%"), fill: 'tozeroy'};
	var broke = {x: age_array, y: broke_array.slice(), name:'破产', fillcolor: 'rgba(217,83,79,.9)', mode:'none',hoverinfo:'x+name+text', text: formatElements(broke_array,1,"%"), fill: 'tonexty'};
	var belowstart = {x: age_array, y: belowthreshold_array.slice(), fillcolor: 'rgba(139,230,139,.9)', mode:'none',name:'余额 < 初始值', hoverinfo:'x+name+text', text: formatElements(belowthreshold_array,1,"%"), fill: 'tonexty'};
	var okay= {x: age_array, y: okay_array.slice(), name:okaylabel, fillcolor: 'rgba(82,184,82,.8)', mode:'none',hoverinfo:'x+name+text', text: formatElements(okay_array,1,"%"), fill: 'tonexty'};
	var great= {x: age_array, y: great_array.slice(), name:'余额 > 2x 初始值', fillcolor: 'rgba(20,124,20,.8)', mode:'none',hoverinfo:'x+name+text', text: formatElements(great_array,1,"%"), fill: 'tonexty'};
	var awesome= {x: age_array, y: awesome_array.slice(), name:'余额 > 5x 初始值', fillcolor: 'rgba(6,68,8,.8)', mode:'none',hoverinfo:'x+name+text', text: formatElements(awesome_array,1,"%"), fill: 'tonexty'};
	var traces=[];
		(showdeath?traces.push(dead):"");
		traces.push(broke);
		(showbelow?traces.push(belowstart):"");
		traces.push(okay);
		(showtwoX?traces.push(great):"");
		(showfiveX?traces.push(awesome):"");		

	function stackedArea(traces) {
	    for(var i=1; i<traces.length; i++) {
	        for(var j=0; j<(Math.min(traces[i]['y'].length, traces[i-1]['y'].length)); j++) {
	            traces[i]['y'][j] += traces[i-1]['y'][j];
	        }
	    }
	    return traces;
	}

	var layout = {
		
		height: 450,
		showlegend: true,
		paper_bgcolor: '#eeeeee',
  		plot_bgcolor: '#eeeeee',
		legend: {
			"orientation": "h",
			x: 0,
			y: (smallscreen?1.2:1.09),
			font: {
		      family: 'Arial',
		      size: (smallscreen?11:12),
		    },
		    tracetoggle: false,
		    bgcolor: 'rgba(255,255,255,0)',
		},
		margin: {l: 40,r: 20,b: 45,
			t: 20,
			pad: 4},
  		xaxis: {
		    title: 'Age-年龄',
		    fixedrange: true,
		    tickfont: {
		      family: 'Arial',
		      size: 11,
		      color: 'black'
		    },
		    titlefont: {
		      family: 'Arial',	
		      size: 14,
		      color: '#7f7f7f'
		    },
		    tick0: 0,
		    dtick: 5,
		    ticklen:2,
		
		    showgrid:false,
		  },
		  yaxis: {
		    title: 'Frequency-概率 (%)',
		    fixedrange: true,
		    titlefont: {
		      family: 'Arial',	
		      size: 15,
		      color: '#7f7f7f'
		    },
		    tickfont: {
		      family: 'Arial',
		      size: 11,
		      color: 'black'
		    },
		    ticks: 'inside',
		    tick0: 0,
		    dtick: 10,
		    ticklen: 2,
		    showgrid:false,
		    range: [0, 100]
		    
		  },
		  annotations: [{
			    xref: 'paper',
			    yref: 'paper',
			    x: 1,
			    xanchor: 'right',
			    y: -.13,
			    yanchor: 'bottom',
			    text: '<a href="RBoD-r2.html" target="_self">原版页面</a>&nbsp;<a href="https://engaging-data.com/will-money-last-retire-early/"><b>engaging-data.com</b></a>',
			    showarrow: false,
			    font: {
			        family: 'Arial',
			        size: (smallscreen?13:16),
			        color: 'darkblue',

			      }
			  },
			  {
			    xref: 'paper',
			    yref: 'paper',
			    x: .47,
			    xanchor: 'center',
			    y: (smallscreen?1.23:1.12),
			    yanchor: 'top',//withdrawal rate、success rate
			    text: "<b>"+grapharray[2]+" / "+grapharray[0]+" 提取率  / "+grapharray[1]+" / "+grapharray[3]+" 成功率 </b>",
			    showarrow: false,
			    font: {
			        family: 'Arial',
			        size: (smallscreen?11:15),
			        

			      }
			  }]
	};


Plotly.newPlot(myDiv, stackedArea(traces), layout,{responsive: true,modeBarButtonsToRemove: ['sendDataToCloud','autoScale2d','resetScale2d','hoverClosestCartesian','hoverCompareCartesian',"toggleSpikelines","zoom2d", "pan2d", "select2d","zoomIn2d", "zoomOut2d",]} );


}